﻿// $Id: PolicyServer.cs 11 2008-08-20 15:24:25Z jkingry $

namespace Mithril.Server
{
    using System;
    using System.IO;
    using System.Net;
    using System.Net.Sockets;

    // Encapsulate and manage state for a single connection from a client 
    class PolicyConnection
    {
        private Socket connection;

        // buffer to receive the request from the client 
        private byte[] buffer;
        private int received;

        // the policy to return to the client 
        private byte[] policy;

        // the request that we're expecting from the client 
        private const string policyRequestString = "<policy-file-request/>";

        public PolicyConnection(Socket client, byte[] policy)
        {
            this.connection = client;
            this.policy = policy;

            this.buffer = new byte[policyRequestString.Length];
            this.received = 0;

            try
            {
                // receive the request from the client 
                this.connection.BeginReceive(this.buffer, 0, policyRequestString.Length, SocketFlags.None, new AsyncCallback(OnReceive), null);
            }
            catch (SocketException)
            {
                this.connection.Close();
            }
        }


        // Called when we receive data from the client 
        private void OnReceive(IAsyncResult res)
        {
            try
            {
                this.received += this.connection.EndReceive(res);

                // if we haven't gotten enough for a full request yet, receive again 
                if (this.received < policyRequestString.Length)
                {
                    this.connection.BeginReceive(this.buffer, this.received, policyRequestString.Length - this.received, SocketFlags.None, new AsyncCallback(OnReceive), null);
                    return;
                }

                // make sure the request is valid 
                string request = System.Text.Encoding.UTF8.GetString(this.buffer, 0, this.received);

                if (StringComparer.InvariantCultureIgnoreCase.Compare(request, policyRequestString) != 0)
                {
                    this.connection.Close();
                    return;
                }


                // send the policy 
                this.connection.BeginSend(this.policy, 0, this.policy.Length, SocketFlags.None, new AsyncCallback(OnSend), null);

            }
            catch (SocketException)
            {
                this.connection.Close();
            }
        }


        // called after sending the policy to the client; close the connection. 
        public void OnSend(IAsyncResult res)
        {
            try
            {
                this.connection.EndSend(res);
            }
            finally
            {
                this.connection.Close();
            }
        }

    }


    // Listens for connections on port 943 and dispatches requests to a PolicyConnection 
    class PolicyServer
    {
        private Socket listener;
        private byte[] policy;

        public const int PolicyServerPort = 943;


        public PolicyServer(byte[] policy)
        {
            this.policy = policy;
        }

        // pass in the path of an XML file containing the socket policy 
        public PolicyServer(string policyFile)
        {
            // Load the policy file 
            this.policy = File.ReadAllBytes(policyFile);
        }

        public void Start()
        {
            // Create the Listening Socket 
            this.listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

            this.listener.Bind(new IPEndPoint(IPAddress.Any, PolicyServer.PolicyServerPort));
            this.listener.Listen(10);

            this.listener.BeginAccept(new AsyncCallback(OnConnection), null);
        }

        public void Stop()
        {
            this.listener.Close();
        }

        // Called when we receive a connection from a client 
        public void OnConnection(IAsyncResult res)
        {
            Socket client = null;

            try
            {
                client = this.listener.EndAccept(res);
            }
            catch (ObjectDisposedException)
            {
                return;
            }
            catch (SocketException)
            {
                return;
            }

            // handle this policy request with a PolicyConnection 
            PolicyConnection pc = new PolicyConnection(client, this.policy);

            // look for more connections 
            this.listener.BeginAccept(new AsyncCallback(OnConnection), null);
        }
        public void Close()
        {
            this.listener.Close();
        }
    }
}

